package io.gitee.zhangbinhub.admin.oauth.controller.api

import cn.dev33.satoken.annotation.SaCheckRole
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts
import cn.dev33.satoken.stp.StpUtil
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.http.HttpHeaders
import io.gitee.zhangbinhub.acp.boot.http.HttpStatus
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.boot.vo.ErrorVo
import io.gitee.zhangbinhub.acp.cloud.resource.server.tools.TokenTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.common.api.OauthApi
import io.gitee.zhangbinhub.admin.common.constant.RoleCode
import io.gitee.zhangbinhub.admin.common.vo.InfoVo
import io.gitee.zhangbinhub.admin.oauth.constant.OauthConstant
import io.gitee.zhangbinhub.admin.oauth.service.ApplicationService
import io.gitee.zhangbinhub.admin.oauth.service.UserService
import io.gitee.zhangbinhub.admin.oauth.vo.OnlineInfoVo
import io.gitee.zhangbinhub.admin.resource.server.base.BaseResourceServerController
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
import org.noear.solon.Solon
import org.noear.solon.annotation.Body
import org.noear.solon.annotation.Controller
import org.noear.solon.annotation.Mapping
import org.noear.solon.annotation.Path
import org.noear.solon.core.handle.MethodType
import org.noear.solon.core.util.MimeType
import org.noear.solon.net.http.HttpUtils
import org.noear.solon.validation.annotation.NotEmpty
import org.noear.solon.validation.annotation.NotNull
import org.noear.solon.validation.annotation.Valid

@Valid
@Controller
@Api("登录信息")
open class TokenController(
    private val logAdapter: LogAdapter,
    private val applicationService: ApplicationService,
    private val userService: UserService,
) : BaseResourceServerController(logAdapter) {
    @Throws(WebException::class)
    private fun innerToken(paramMap: MutableMap<String, Any>): String {
        val clientId = paramMap[OauthConstant.clientIdKey]?.toString()
        if (CommonTools.isNullStr(clientId)) {
            throw WebException("${OauthConstant.clientIdKey} 不能为空")
        }
        val clientSecret = paramMap[OauthConstant.clientSecretKey]?.toString()
        if (CommonTools.isNullStr(clientSecret)) {
            throw WebException("${OauthConstant.clientSecretKey} 不能为空")
        }
        return try {
            HttpUtils.http(Solon.cfg().appGroup(), Solon.cfg().appName(), SaOAuth2Consts.Api.token)
                .timeout(60)
                .header(HttpHeaders.CONTENT_TYPE, MimeType.APPLICATION_FORM_URLENCODED_VALUE)
                .data(paramMap)
                .exec("POST").also { response ->
                    if (response.code() != HttpStatus.OK.value()) {
                        val errorVo = response.bodyAsBean<ErrorVo>(ErrorVo::class.java)
                        throw WebException(HttpStatus.UNAUTHORIZED, errorVo.errorDescription)
                    }
                }.bodyAsString()
        } catch (e: Exception) {
            throw WebException(HttpStatus.UNAUTHORIZED, e.message)
        }
    }

    @ApiOperation("申请token")
    @Mapping(
        method = [MethodType.POST],
        value = OauthApi.basePath + OauthApi.token,
        produces = MimeType.APPLICATION_JSON_VALUE
    )
    @Throws(WebException::class)
    fun doLogin(@Body paramMap: MutableMap<String, Any>): String = innerToken(paramMap)

    @ApiOperation("注销当前用户")
    @Mapping(
        method = [MethodType.POST],
        value = OauthApi.basePath + OauthApi.logOut,
        produces = MimeType.APPLICATION_JSON_VALUE
    )
    @Throws(WebException::class)
    fun doLogOut(): InfoVo =
        try {
            logAdapter.info("用户[loginNo=" + StpUtil.getLoginIdAsString() + "]主动下线!")
            TokenTools.revokeAccessToken()
            InfoVo(message = "成功下线")
        } catch (e: Exception) {
            throw WebException(e.message)
        }

    @ApiOperation("获取各应用在线用户数统计")
    @Mapping(
        method = [MethodType.GET],
        value = OauthApi.basePath + OauthApi.onlineInfo,
        produces = MimeType.APPLICATION_JSON_VALUE
    )
    @Throws(WebException::class)
    fun getOnlineInfo(): List<OnlineInfoVo> = try {
        mutableListOf<OnlineInfoVo>().apply {
            val infoList = StpUtil.searchTokenValue("", 0, -1, true).map { tokenValue ->
                StpUtil.getTerminalInfoByToken(tokenValue.replace(StpUtil.stpLogic.splicingKeyTokenValue(""), ""))
            }
            applicationService.getOwnAppList(StpUtil.getLoginIdAsString()).forEach {
                this.add(
                    OnlineInfoVo(
                        appId = it.id,
                        appName = it.appName,
                        count = infoList.filter { info -> info?.deviceType == it.id }.size.toLong()
                    )
                )
            }
        }
    } catch (e: Exception) {
        throw WebException(e.message)
    }

    @ApiOperation("指定应用下的用户强制下线")
    @SaCheckRole(RoleCode.SUPER)
    @Mapping(
        method = [MethodType.DELETE],
        value = OauthApi.basePath + OauthApi.onlineInfo + "/{appId}",
        produces = MimeType.APPLICATION_JSON_VALUE
    )
    @Throws(WebException::class)
    fun delete(
        @ApiParam("应用id", required = true)
        @Path(name = "appId")
        appId: String,
        @ApiParam("用户id列表", required = true)
        @NotEmpty(message = "id不能为空")
        @NotNull(message = "id不能为空")
        @Body
        idList: List<String>
    ): InfoVo = try {
        idList.forEach {
            val userInfo = userService.getUserInfoById(it) ?: throw WebException("找不到该用户信息")
            TokenTools.revokeAccessToken(appId, userInfo.loginNo)
            logAdapter.info("用户[" + userInfo.name + "(" + userInfo.loginNo + ")]被管理员强制下线!")
        }
        InfoVo(message = "成功下线")
    } catch (e: Exception) {
        throw WebException(e.message)
    }

}
